package org.arl.modem.transportARQ;

import jade.core.AID;
import jade.lang.acl.MessageTemplate;

import java.util.Random;

import org.arl.modem.ATCmd;
import org.arl.modem.ATCommand;
import org.arl.modem.ATCommandInterpreter;
import org.arl.modem.ATNtf;
import org.arl.modem.CommandHandler;
import org.arl.modem.FAPIMessage;

public class TransportARQCommands extends CommandHandler {
   private AID tsp;

   public TransportARQCommands(ATCommandInterpreter agent) {
      super("~T", agent);
      agent.subscribeTopic(TransportARQ.NOTIFICATIONS);
      this.tsp = new AID(agent.getAgentProperty("tsp", "tsp"), AID.ISLOCALNAME);

   }

   @ATCmd("AT~TP? - list parameter names")
   public boolean Pq(ATCommand c) {
      output(getParamHelp(TransportParam.class));
      return true;
   }

   @ATCmd("AT~TP.param=value - set parameter\n"
         + "AT~TP.param? - get parameter")
   // / @see TransportParam for list of supported parameters.
   public boolean P_(ATCommand c) {
      return handleParams(tsp, TransportParam.class, c);
   }

   @ATCmd("AT~TTX.PKT=src,dest,len - transmit packet")
   public boolean TX_PKTz(ATCommand c) {
      return doTxPkt(c);
   }

   @ATCmd("AT~TTX.TESTPKT=src,dest[,len] - transmit a test packet")
   public boolean TX_TESTPKTz(ATCommand c) {
      log.fine("in command testpkt");

      return doTxPktTest(c);

   }

   @ATCmd("AT~TTX.FILE=src,dest,filepath - transmit a test packet")
   public boolean TX_FILEz(ATCommand c) {
      log.fine("in command tx file");

      return doTxFile(c);

   }

   @ATCmd("AT~TTX.REMOTE=src,dest - Execute any AT command remotely at dest")
   public boolean TX_REMOTE(ATCommand c) {

      return txRemote(c);

   }

   /**
    * test command AT for AT
    * @param c
    * @return
    */
   // FIXME Rohit, Jan 12, 2012: not completed or tested yet
   private boolean txRemote(ATCommand c) {
      FAPIMessage msg = agent.createMessage(FAPIMessage.REQUEST, tsp);
      msg.add("dest", c.param[1]);
      msg.add("src", c.param[0]);
      msg.add("RemoteExecution", 1);
      byte[] data = readStringData();
      // byte[] data = readHexData(20);
      String s = new String(data);
      log.fine("data input by user at command:length" + s+":"+data.length);
      msg.setData(data);

      sendAndCheckAck(msg);
      return true;
   }

   /**
    * test method
    * @return
    */
   // FIXME Rohit, Jan 12, 2012: created for AT command for AT commands: not
   // tested yet problem with reading String input by user
   protected byte[] readStringData() {
      StringBuffer buf = new StringBuffer();
      MessageTemplate mt = MessageTemplate
            .MatchPerformative(FAPIMessage.REQUEST);
      // while (buf.length() < 2 * len) {
      FAPIMessage msg = new FAPIMessage(agent.blockingReceive(mt));
      String s = msg.getContent();
      if (s.trim().length() == 0) {
         error("Insufficient data");
         return null;
      }
      buf.append(s);
      String commandinHex = convertStringToHex(buf.toString());
      return commandinHex.getBytes();
      

   }

   public String convertStringToHex(String str) {

      char[] chars = str.toCharArray();

      StringBuffer hex = new StringBuffer();
      for (int i = 0; i < chars.length; i++) {
         hex.append(Integer.toHexString((int) chars[i]));
      }

      return hex.toString();
   }

   public static String convertHexToString(String hex) {

      StringBuilder sb = new StringBuilder();
      StringBuilder temp = new StringBuilder();
      for (int i = 0; i < hex.length() - 1; i += 2) {
         // grab the hex in pairs
         String output = hex.substring(i, (i + 2));
         // convert hex to decimal
         int decimal = Integer.parseInt(output, 16);
         // convert the decimal to character
         sb.append((char) decimal);

         temp.append(decimal);
      }
      return sb.toString();
   }

   @ATCmd("AT~TTX.STOP - Stop all transport layer (Re)Transmissions")
   public boolean TX_STOP(ATCommand c) {

      return stopTx(c);

   }

   private boolean stopTx(ATCommand c) {
      FAPIMessage msg = agent.createMessage(FAPIMessage.REQUEST, tsp);
      msg.add("STOP", 1);
      return sendAndCheckAck(msg);

   }

   private boolean doTxPktTest(ATCommand c) {
      FAPIMessage msg = agent.createMessage(FAPIMessage.REQUEST, tsp);
      if (c.param.length == 3) {

         msg.add("len", c.param[2]);
         Random r = new Random();
         int[] intData = new int[c.param[2] * 2];
         byte[] bytes = new byte[c.param[2]];
         StringBuffer buf = new StringBuffer(2 * c.param[2]);
         for (int i = 0; i < c.param[2] * 2; i++) {
            intData[i] = r.nextInt(10);
            buf.append(i);
         }
         System.out.println();
         Random r1 = new Random();
         r1.nextBytes(bytes);
         // for (byte b : bytes) {
         // System.out.print(Integer.toString(b, 16).toUpperCase());
         // }
         System.out.println(formatBytes(bytes, 0, bytes.length));
         log.fine("Random Data=");
         log.fine(formatBytes(bytes, 0, bytes.length));
         // try {
         // for (int i = 0; i < c.param[2]; i++) {
         // int a = Integer.parseInt(buf.substring(2 * i, 2 * i + 2), 16);
         // bytes[i] = (byte) a;
         // System.out.print(a);
         // }
         // } catch (NumberFormatException ex) {
         // error("Could not interpret data as hexadecimal");
         // return false;
         // }
         System.out.println();

         msg.setData(bytes);
         // String s = new String(bytes);
         // System.out.println(String.fo)
         // log.fine("Random data by at command:" + s);
      } else {
         log.warning("Error:input argument not proper");

         msg.add("len", 0);
         msg.setData(new byte[0]);
      }
      msg.add("src", c.param[0]);
      msg.add("dest", c.param[1]);
      msg.add("tx", 1);

      return sendAndCheckAck(msg);

   }

   private boolean doTxFile(ATCommand c) {
      FAPIMessage msg = agent.createMessage(FAPIMessage.REQUEST, tsp);
      if (c.param.length == 2) {
         MessageTemplate mt = MessageTemplate
               .MatchPerformative(FAPIMessage.REQUEST);
         // while (buf.length() < 2 * len) {
         FAPIMessage msg1 = new FAPIMessage(agent.blockingReceive(mt));
         String filename = msg1.getContent();
         System.out.println("path enterd:" + filename);
         String commandinHex = convertStringToHex(filename);
         byte[] bytes = commandinHex.getBytes();

         msg.setData(bytes);
         msg.add("file", 1);
      } else {
         log.warning("Error:input argument not proper");

         msg.add("len", 0);
         msg.setData(new byte[0]);
      }
      msg.add("src", c.param[0]);
      msg.add("dest", c.param[1]);
      msg.add("tx", 1);

      return sendAndCheckAck(msg);

   }

   @ATNtf("~TRX.DATA=src,dst,len,pktnumber - received transport layer DATA packet\n"
         + "~TRX.ACK=src,dst,len - received transport layer ACK packet\n")
   public boolean processNotification(FAPIMessage msg) {
      if (!msg.getSender().equals(tsp))
         return false;
      int rx = msg.getAsInteger("RX", 0);
      int done = msg.getAsInteger("done", 0);
      String type = msg.get("TYPE");
      if (rx == 1) {

         byte[] data = msg.getData();
         StringBuffer s = new StringBuffer();
         s.append("~T" + type + "=");
         s.append(msg.get("src"));
         s.append(',');
         s.append(msg.get("dest"));
         s.append(',');
         s.append((data == null) ? 0 : data.length);
         if (data != null) {

            s.append('\n');
            s.append(formatBytes(data, 0, data.length));
            // s.append('\n');
            // s.append(new String(data));
         }

         output(s.toString());
      } else {

         StringBuffer s = new StringBuffer();

         s.append("~T" + type + "=");
         if (msg.getData() != null) {
            byte[] data = msg.getData();
            s.append('\n');
            s.append(formatBytes(data, 0, data.length));
         }
         output(s.toString());
      }
      if (done == 1) {
         output(msg.get("TYPE") + ":" + msg.get("msg"));
      }

      return true;
   }

   public boolean doTxPkt(ATCommand c) {
      FAPIMessage msg = agent.createMessage(FAPIMessage.REQUEST, tsp);
      msg.add("src", c.param[0]);
      msg.add("dest", c.param[1]);
      // msg.add("len", c.param[2]);
      msg.add("tx", 1);
      byte[] data = readHexData(c.param[2]);
      String s = new String(data);
      log.fine("data input by user at command:" + s);
      msg.setData(data);

      return sendAndCheckAck(msg);

   }

}
